﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public static class SerialComm
{
    public static SerialPort _serialPort;
    public static byte[] _buffer = new byte[1024];
    public static int _bufferIndex = 0;
    public static void initDevis()
    {

        // 默认通信参数
        // 串口号、波特率、奇偶校验、数据位、停止位初始化SerialPort类的新实例。
        _serialPort = new SerialPort("COM3", 38400, Parity.None, 8, StopBits.One)
        {
            Handshake = Handshake.None,
            ReadTimeout = 500,    // 读取超时时间
            WriteTimeout = 500,   // 写入超时时间

            RtsEnable = true
            /*ReadBufferSize = 1000,   // 设置缓冲区大小，默认 4096
            RtsEnable = true,*/
            // ReceivedBytesThreshold = 1, // 只要有一个数据就触发


        };

        _serialPort.Open();
        // 在初始化时注册事件处理程序
        // _serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        Thread thread = new Thread(new ThreadStart(DataReceived));
        thread.Start();

        UnityEngine.Debug.Log("0000000000000");
    }

    public static void DataReceived()
    {
        while (true)
        {
            if (_serialPort.IsOpen)
            {
                int count = _serialPort.BytesToRead;
                if (count > 0)
                {

                    byte[] readBuffer = new byte[count];  //数据 也是byte[]类型
                    try
                    {
                        _serialPort.Read(readBuffer, 0, count);

                        //数据处理
                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < readBuffer.Length; i++)
                        {
                            sb.AppendFormat("{0:x2}" + "", readBuffer[i]);//{0:X2}将第一个参数转换成大写的十六进制文本,长度为2,不足前面补0
                        }
                        UnityEngine.Debug.Log("OLD" + sb.ToString());
                    }
                    catch (Exception ex)
                    {
                        UnityEngine.Debug.Log(ex.Message);
                    }


                    //byte[] readBuffer = new byte[count];  //数据 也是byte[]类型
                    //try
                    //{
                    //    _serialPort.Read(readBuffer, 0, count);

                    //    // 读取数据
                    //    int bytesToRead = _serialPort.BytesToRead;
                    //    byte[] tempBuffer = new byte[bytesToRead];
                    //    _serialPort.Read(tempBuffer, 0, bytesToRead);

                    //    // string receivedData = BitConverter.ToString(tempBuffer).Replace("-", " ");
                    //    // Console.WriteLine($"接收到数据：{receivedData}");
                    //    UnityEngine.Debug.Log($"接收到数据：{tempBuffer}");

                    //}
                    //catch (Exception ex)
                    //{
                    //    UnityEngine.Debug.Log(ex.Message);
                    //}
                }
            }
            Thread.Sleep(10);
        }
    }

    /// <summary>
    /// 打开串口
    /// </summary>
    public static void OpenPort()
    {

        if (!_serialPort.IsOpen)
        {
            try
            {

                _serialPort.Open();
                UnityEngine.Debug.Log("串口打开成功！");
            }
            catch (Exception ex)
            {
                UnityEngine.Debug.Log($"串口打开失败：{ex.Message}");
            }
        }
    }

    /// <summary>
    /// 关闭串口
    /// </summary>
    public static void ClosePort()
    {
        if (_serialPort.IsOpen)
        {
            _serialPort.Close();
            Console.WriteLine("串口已关闭");
        }
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="data">十六进制字符串数据</param>
    public static void SendData(string data)
    {
        if (_serialPort.IsOpen)
        {
            byte[] buffer = StringToByteArray(data);
            _serialPort.Write(buffer, 0, buffer.Length);
            UnityEngine.Debug.Log($"已发送: {data}");

        }
        else
        {
            UnityEngine.Debug.Log("串口未打开，无法发送数据。");
        }
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="data">十六进制字符串数据</param>
    public static void SendDataASC(string data)
    {
        if (_serialPort.IsOpen)
        {
            _serialPort.Write(data);
            UnityEngine.Debug.Log($"已发送: {data}");
        }
        else
        {
            UnityEngine.Debug.Log("串口未打开，无法发送数据。");
        }
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="buffer"></param>
    public static void SendData(byte[] buffer)
    {
        if (_serialPort.IsOpen)
        {
            _serialPort.Write(buffer, 0, buffer.Length);
            Console.WriteLine($"已发送: {buffer}");
        }
        else
        {
            Console.WriteLine("串口未打开，无法发送数据。");
        }
    }

    /// <summary>
    /// 接收数据处理程序
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        UnityEngine.Debug.Log("7777777777777");

        SerialPort sp = (SerialPort)sender;

        // 读取数据
        int bytesToRead = sp.BytesToRead;
        byte[] tempBuffer = new byte[bytesToRead];
        sp.Read(tempBuffer, 0, bytesToRead);

        string receivedData = BitConverter.ToString(tempBuffer).Replace("-", " ");
        // Console.WriteLine($"接收到数据：{receivedData}");
        // UnityEngine.Debug.Log(receivedData);



        // 将接收到的数据添加到缓冲区
        Array.Copy(tempBuffer, 0, _buffer, _bufferIndex, bytesToRead);
        _bufferIndex += bytesToRead;

        if (_bufferIndex >= 5)
        {
            int byteCount = _buffer[2];
            int expectedLength = 3 + byteCount + 2;

            if (_bufferIndex >= expectedLength)
            {
                // 处理完整的报文
                byte[] receivedBuffer = new byte[expectedLength];
                Array.Copy(_buffer, 0, receivedBuffer, 0, expectedLength);

                // 调用解析方法
                ParseModbusResponse(receivedBuffer);

                // 将剩余数据移动到缓冲区的开头
                int remainingBytes = _bufferIndex - expectedLength;
                if (remainingBytes > 0)
                {
                    Array.Copy(_buffer, expectedLength, _buffer, 0, remainingBytes);
                }
                _bufferIndex = remainingBytes;
            }
        }



    }

    /// <summary>
    /// 寄存器开关的储存数组
    /// </summary>
    public static int[] dataNumbers = new int[32];

    /// <summary>
    /// 解析Modbus RTU响应报文
    /// </summary>
    /// <param name="responseBytes">响应报文字节数组</param>
    private static void ParseModbusResponse(byte[] responseBytes)
    {
        if (responseBytes.Length < 5)
        {
            Console.WriteLine("无效的响应长度");
            return;
        }

        byte deviceAddress = responseBytes[0];
        byte functionCode = responseBytes[1];
        byte byteCount = responseBytes[2];

        Console.WriteLine("报文解析为：====================================");
        Console.WriteLine($"设备地址: {deviceAddress}");
        Console.WriteLine($"功能码: {functionCode}");
        Console.WriteLine($"字节数: {byteCount}");
        

        
        if (responseBytes.Length != 3 + byteCount + 2)
        {
            Console.WriteLine("无效的响应字节数");
            return;
        }

        // 清空dataNumbers数组
        Array.Clear(dataNumbers, 0, dataNumbers.Length);


        /*for (int i = 3; i < 3 + byteCount; i += 2)
        {
            int registerValue = (responseBytes[i] << 8) | responseBytes[i + 1];
            dataNumbers[i] = registerValue;

            Console.WriteLine($"寄存器值: {registerValue}");
            Console.WriteLine($"dataNumbers的数据为: {dataNumbers[i]}");
            // Console.WriteLine($"寄存器值的类型为: {registerValue.GetType().FullName}");

        }*/

        for (int i = 0, j = 3; i < dataNumbers.Length && j < 3 + byteCount; i++, j += 2)
        {
            int registerValue = (responseBytes[j] << 8) | responseBytes[j + 1];
            dataNumbers[i] = registerValue;

            Console.WriteLine($"寄存器值: {registerValue}");
            // Console.WriteLine($"dataNumbers{i}的值为：{dataNumbers[i]}");
        }



        // CRC校验码
        byte crcLow = responseBytes[responseBytes.Length - 2];
        byte crcHigh = responseBytes[responseBytes.Length - 1];
        Console.WriteLine($"CRC校验码: {crcHigh:X2}{crcLow:X2}");

    }

    /// <summary>
    /// 将十六进制字符串转换为字节数组
    /// </summary>
    /// <param name="hex">十六进制字符串</param>
    /// <returns>字节数组</returns>
    private static byte[] StringToByteArray(string hex)
    {
        hex = hex.Replace(" ", "");
        byte[] bytes = new byte[hex.Length / 2];
        for (int i = 0; i < hex.Length; i += 2)
        {
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        }
        return bytes;
    }

    private static byte[] _sendIndex = { 0x01, 0x05, 0x00, 0x02, 0x00, 0x00 };  //关闭1

    /// <summary>
    /// 串口屏实时更新数据GetScreenInput
    /// </summary>
    /// <param name="args"></param>
    /// <returns></returns>
    /*static async Task Main(string[] args)
    {

        SerialComm sc = new SerialComm();
        await Task.Delay(2000); // 等待2秒

        sc.OpenPort();

        for (int i = 0; i < 10; i++)
        {
            sc.SendDataASC(GetScreenInput("PI1002", new Random().Next(10, 100).ToString()));
            Thread.Sleep(500);
        }


        Console.ReadKey();

        // sc.ClosePort();
    }*/

   /* static async Task Main(string[] args)
    {

        *//*SerialComm sc = new SerialComm();
        await Task.Delay(2000); // 等待2秒
        sc.OpenPort();


        sc.SendData("03 04 00 00 00 20 F0 30");
        await Task.Delay(50);
        Console.WriteLine($"寄存器13位置为：{SerialComm.dataNumbers[13]}");
        await Task.Delay(50);
        sc.SendData("03 04 00 00 00 20 F0 30");
        await Task.Delay(50);
        Console.WriteLine($"寄存器13位置为：{SerialComm.dataNumbers[13]}");
        await Task.Delay(50);
        sc.SendData("03 04 00 00 00 20 F0 30");
        await Task.Delay(50);
        Console.WriteLine($"寄存器13位置为：{SerialComm.dataNumbers[13]}");
        await Task.Delay(50);
        sc.SendData("03 04 00 00 00 20 F0 30");
        await Task.Delay(50);
        Console.WriteLine($"寄存器13位置为：{SerialComm.dataNumbers[13]}");
        await Task.Delay(50);
        sc.SendData("03 04 00 00 00 20 F0 30");
        await Task.Delay(50);
        Console.WriteLine($"寄存器13位置为：{SerialComm.dataNumbers[13]}");
        await Task.Delay(50);
*//*
        // sc.SendData("01 04 00 00 00 20 F1 D2");

        // Console.WriteLine("按任意键退出程序...");


        // var crcArr = CRC16.CRCCalcWithOri(_sendIndex);    // 关闭
        // sc.SendData(GetRelayInput(1, 2, false));


        // Console.ReadKey();

        // sc.ClosePort();
    }
*/



    /// <summary>
    /// 获取继电器输入信号 写入单个线圈
    /// </summary>
    /// <param name="add">地址</param>
    /// <param name="index">位置</param>
    /// <param name="isOpen">开启 或 关闭</param>
    /// <returns></returns>
    private static byte[] GetRelayInput(byte add, byte index, bool isOpen)
    {
        // return CRC16.CRCCalcWithOri([0x01, 0x05, 0x00, index, (byte)(isOpen ? 0xFF : 0x00), 0x00]);//关闭1
        return CRC16.CRCCalcWithOri(new byte[] { 0x01, 0x05, 0x00, index, (byte)(isOpen ? 0xFF : 0x00), 0x00});
    }

    /// <summary>
    /// 获取屏幕显示数值
    /// </summary>
    /// <param name="val">串口屏的数据</param>
    /// <returns></returns>
    private static string GetScreenInput(string name, string val)
    {
        return $"wset {name}PV.txt \"{val}\" \r\n";   // 对应串口屏 PV 的 val 值变更
    }


}


#region 16位CRC校验
public static class CRC16
{
    /// <summary>
    /// CRC校验，参数data为byte数组
    /// </summary>
    /// <param name="data">校验数据，字节数组</param>
    /// <returns>字节0是高8位，字节1是低8位</returns>
    public static byte[] CRCCalc(byte[] data)
    {
        //crc计算赋初始值
        int crc = 0xffff;
        for (int i = 0; i < data.Length; i++)
        {
            crc = crc ^ data[i];
            for (int j = 0; j < 8; j++)
            {
                int temp;
                temp = crc & 1;
                crc = crc >> 1;
                crc = crc & 0x7fff;
                if (temp == 1)
                {
                    crc = crc ^ 0xa001;
                }
                crc = crc & 0xffff;
            }
        }
        //CRC寄存器的高低位进行互换
        byte[] crc16 = new byte[2];
        //CRC寄存器的高8位变成低8位，
        crc16[1] = (byte)((crc >> 8) & 0xff);
        //CRC寄存器的低8位变成高8位
        crc16[0] = (byte)(crc & 0xff);
        return crc16;
    }

    public static byte[] CRCCalcWithOri(byte[] data)
    {
        //crc计算赋初始值
        int crc = 0xffff;
        for (int i = 0; i < data.Length; i++)
        {
            crc = crc ^ data[i];
            for (int j = 0; j < 8; j++)
            {
                int temp;
                temp = crc & 1;
                crc = crc >> 1;
                crc = crc & 0x7fff;
                if (temp == 1)
                {
                    crc = crc ^ 0xa001;
                }
                crc = crc & 0xffff;
            }
        }
        //CRC寄存器的高低位进行互换
        byte[] crc16 = new byte[data.Length + 2];
        for (int i = 0; i < data.Length; i++)
        {
            crc16[i] = data[i];
        }
        //CRC寄存器的高8位变成低8位，
        crc16[data.Length + 1] = (byte)((crc >> 8) & 0xff);
        //CRC寄存器的低8位变成高8位
        crc16[data.Length] = (byte)(crc & 0xff);
        return crc16;
    }

    /// <summary>
    /// CRC校验，参数为空格或逗号间隔的字符串
    /// </summary>
    /// <param name="data">校验数据，逗号或空格间隔的16进制字符串(带有0x或0X也可以),逗号与空格不能混用</param>
    /// <returns>字节0是高8位，字节1是低8位</returns>
    public static byte[] CRCCalc(string data)
    {
        //分隔符是空格还是逗号进行分类，并去除输入字符串中的多余空格
        IEnumerable<string> datac = data.Contains(",") ? data.Replace(" ", "").Replace("0x", "").Replace("0X", "").Trim().Split(',') : data.Replace("0x", "").Replace("0X", "").Split(' ').ToList().Where(u => u != "");
        List<byte> bytedata = new List<byte>();
        foreach (string str in datac)
        {
            bytedata.Add(byte.Parse(str, System.Globalization.NumberStyles.AllowHexSpecifier));
        }
        byte[] crcbuf = bytedata.ToArray();
        //crc计算赋初始值
        return CRCCalc(crcbuf);
    }

    /// <summary>
    ///  CRC校验，截取data中的一段进行CRC16校验
    /// </summary>
    /// <param name="data">校验数据，字节数组</param>
    /// <param name="offset">从头开始偏移几个byte</param>
    /// <param name="length">偏移后取几个字节byte</param>
    /// <returns>字节0是高8位，字节1是低8位</returns>
    public static byte[] CRCCalc(byte[] data, int offset, int length)
    {
        byte[] Tdata = data.Skip(offset).Take(length).ToArray();
        return CRCCalc(Tdata);
    }

}
#endregion
